// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build windows
// +build windows

package win注册表类

import (
	"fmt"
	"golang.org/x/sys/windows"
	"golang.org/x/sys/windows/registry"
	"syscall"
	"time"
)

const (
	REG_NOTIFY_CHANGE_NAME       = 0x00000001
	REG_NOTIFY_CHANGE_ATTRIBUTES = 0x00000002
	REG_NOTIFY_CHANGE_LAST_SET   = 0x00000004
	REG_NOTIFY_CHANGE_SECURITY   = 0x00000008
)

var (
	advapi32DLL                 = windows.NewLazySystemDLL("advapi32.dll")
	procRegNotifyChangeKeyValue = advapi32DLL.NewProc("RegNotifyChangeKeyValue")
)

func RegNotifyChangeKeyValue(
	key syscall.Handle,
	watchSubtree bool,
	notifyFilter uint32,
	event syscall.Handle,
	asynchronous bool,
) error {
	var watchSubtreeRaw uint32
	if watchSubtree {
		watchSubtreeRaw = 1
	}
	var asynchronousRaw uint32
	if asynchronous {
		asynchronousRaw = 1
	}

	ret, _, _ := procRegNotifyChangeKeyValue.Call(
		uintptr(key),
		uintptr(watchSubtreeRaw),
		uintptr(notifyFilter),
		uintptr(event),
		uintptr(asynchronousRaw),
	)
	if ret != 0 {
		return fmt.Errorf("RegNotifyChangeKeyValue错误: %d", ret)
	}
	return nil
}

// 例子:
//
//	X监测(registry.LOCAL_MACHINE, `SOFTWARE\Autodesk`, func(根目录 registry.Key, 变化路径 string) {
//		fmt.Println(根目录, 变化路径)
//	})
func X监测(根目录 registry.Key, 路径 string, 回调函数 func(根目录 registry.Key, 变化路径 string)) error {
	// 打开注册表项
	key, err := registry.OpenKey(根目录, 路径, registry.READ|registry.NOTIFY)
	if err != nil {
		fmt.Printf("打开注册表失败: (%d).\n", err)
		return err
	}
	defer key.Close()

	// 创建事件对象
	event, err := windows.CreateEvent(nil, 1, 0, nil)
	if err != nil {
		fmt.Printf("创建事件对象失败: (%d).\n", err)
		return err
	}
	defer windows.CloseHandle(event)
	// 使用循环持续监听
	for {
		// 设置监听
		filter := REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
			REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY

		err = RegNotifyChangeKeyValue(
			syscall.Handle(key),
			true,
			uint32(filter),
			syscall.Handle(event),
			true)
		if err != nil {
			fmt.Printf("循环持续监听失败: (%d).\n", err)
			return err
		}

		// 等待事件触发
		fmt.Printf("正在等待指定注册表更改。..\n")
		state, err := windows.WaitForSingleObject(event, windows.INFINITE)
		if err != nil {
			fmt.Printf("WaitForSingleObject中出错 (%d).\n", err)
			return err
		}

		if state == windows.WAIT_OBJECT_0 {
			回调函数(根目录, 路径)
			fmt.Printf("[%s] 检测到注册表变化!\n", time.Now().Format("15:04:05"))
		} else {
			fmt.Printf("WaitForSingleObject中出错 (%d).\n", err)
			return err
		}

		// 重置事件，准备下一次监听
		windows.ResetEvent(event)
	}
}
